package com.gitee.zw.irmp.common.utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

/**
 * You can run native command by this util.
 */
public class CmdUtils {

    ///// the test code
    public static void main(String[] args) {
        String cmd = "cmd.exe dir";

        try {
            NativeCommandResponse response = runNativeCommand(cmd);
            System.out.println(response);
        } catch (NativeCommandRunException e) {
            e.printStackTrace();
        }

    }

    /**
     * execute a native command
     *
     * @param command the command to run
     * @return the result of the command
     * @throws NativeCommandRunException ref {@link NativeCommandRunException}
     */
    public static NativeCommandResponse runNativeCommand(String command) throws NativeCommandRunException {
        if (command == null || command.isEmpty()) {
            throw new NativeCommandRunException("empty command!");
        }
        String[] commandWithArgs = command.split(" ");
        if (commandWithArgs.length == 0) {
            throw new NativeCommandRunException("empty command find!");
        }
        return runNativeCommand(commandWithArgs);
    }

    /**
     * execute a native command
     *
     * @param command the command to run
     * @return the result of the command
     * @throws NativeCommandRunException ref {@link NativeCommandRunException}
     */
    public static NativeCommandResponse runNativeCommand(String... command) throws NativeCommandRunException {
        if (command == null || command.length == 0) {
            throw new NativeCommandRunException("empty command find!");
        }
        Process process;
        try {
            process = Runtime.getRuntime().exec(command);
        } catch (Exception e) {
            throw new NativeCommandRunException(e.getMessage());
        }
        /// read result from process
        List<String> result = new ArrayList<>();
        int exitCode = -1;

        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        try {
            String line;
            while ((line = reader.readLine()) != null) {
                result.add(line);
            }
            /// wait for the process to terminal
            process.waitFor();
            exitCode = process.exitValue();
        } catch (Exception e) {
            throw new NativeCommandRunException(e.getMessage());
        } finally {
            try {
                reader.close();
            } catch (IOException e) {
                // ai
            }
        }
        return new NativeCommandResponse(exitCode, result);
    }


    public static class NativeCommandResponse {
        public NativeCommandResponse(int exitCode, List<String> ret) {
            this.exitCode = exitCode;
            this.ret = ret;
        }

        public NativeCommandResponse(int exitCode) {
            this.exitCode = exitCode;
        }

        public int getExitCode() {
            return exitCode;
        }

        public void setExitCode(int exitCode) {
            this.exitCode = exitCode;
        }

        public List<String> getRet() {
            return ret;
        }

        public void setRet(List<String> ret) {
            this.ret = ret;
        }

        @Override
        public String toString() {
            return "NativeCommandResponse{" +
                    "exitCode=" + exitCode +
                    ", ret=" + ret +
                    '}';
        }

        public boolean isOk() {
            return exitCode == 0;
        }

        private int exitCode;
        private List<String> ret;
    }

    public static class NativeCommandRunException extends Exception {
        /**
         * Constructs a new exception with {@code null} as its detail message.
         * The cause is not initialized, and may subsequently be initialized by a
         * call to {@link #initCause}.
         */
        public NativeCommandRunException() {
            super();
        }

        /**
         * Constructs a new exception with the specified detail message.  The
         * cause is not initialized, and may subsequently be initialized by
         * a call to {@link #initCause}.
         *
         * @param message the detail message. The detail message is saved for
         *                later retrieval by the {@link #getMessage()} method.
         */
        public NativeCommandRunException(String message) {
            super(message);
        }

        /**
         * Constructs a new exception with the specified detail message and
         * cause.  <p>Note that the detail message associated with
         * {@code cause} is <i>not</i> automatically incorporated in
         * this exception's detail message.
         *
         * @param message the detail message (which is saved for later retrieval
         *                by the {@link #getMessage()} method).
         * @param cause   the cause (which is saved for later retrieval by the
         *                {@link #getCause()} method).  (A <tt>null</tt> value is
         *                permitted, and indicates that the cause is nonexistent or
         *                unknown.)
         */
        public NativeCommandRunException(String message, Throwable cause) {
            super(message, cause);
        }

        /**
         * Constructs a new exception with the specified cause and a detail
         * message of <tt>(cause==null ? null : cause.toString())</tt> (which
         * typically contains the class and detail message of <tt>cause</tt>).
         * This constructor is useful for exceptions that are little more than
         * wrappers for other throwables (for example, {@link
         * java.security.PrivilegedActionException}).
         *
         * @param cause the cause (which is saved for later retrieval by the
         *              {@link #getCause()} method).  (A <tt>null</tt> value is
         *              permitted, and indicates that the cause is nonexistent or
         *              unknown.)
         */
        public NativeCommandRunException(Throwable cause) {
            super(cause);
        }

        /**
         * Constructs a new exception with the specified detail message,
         * cause, suppression enabled or disabled, and writable stack
         * trace enabled or disabled.
         *
         * @param message            the detail message.
         * @param cause              the cause.  (A {@code null} value is permitted,
         *                           and indicates that the cause is nonexistent or unknown.)
         * @param enableSuppression  whether or not suppression is enabled
         *                           or disabled
         * @param writableStackTrace whether or not the stack trace should
         *                           be writable
         */
        protected NativeCommandRunException(String message, Throwable cause,
                                            boolean enableSuppression,
                                            boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    }

}
